﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace Zhao.Common.Helper
{
    public static class StringExtend
    {
        #region 正则表达式

        /// <summary>
        /// 指示所指定的正则表达式在指定的输入字符串中是否找到了匹配项
        /// </summary>
        /// <param name="value">要搜索匹配项的字符串</param>
        /// <param name="pattern">要匹配的正则表达式模式</param>
        /// <param name="isContains">是否包含，否则全匹配</param>
        /// <returns>如果正则表达式找到匹配项，则为 true；否则，为 false</returns>
        public static bool IsMatch(this string value, string pattern, bool isContains = true)
        {
            if (value == null)
            {
                return false;
            }
            return isContains
                ? Regex.IsMatch(value, pattern)
                : Regex.Match(value, pattern).Success;
        }

        /// <summary>
        /// 在指定的输入字符串中搜索指定的正则表达式的第一个匹配项
        /// </summary>
        /// <param name="value">要搜索匹配项的字符串</param>
        /// <param name="pattern">要匹配的正则表达式模式</param>
        /// <returns>一个对象，包含有关匹配项的信息</returns>
        public static string Match(this string value, string pattern)
        {
            if (value == null)
            {
                return null;
            }
            return Regex.Match(value, pattern).Value;
        }



        /// <summary>
        /// 在指定的输入字符串中搜索指定的正则表达式的所有匹配项的字符串集合
        /// </summary>
        /// <param name="value"> 要搜索匹配项的字符串 </param>
        /// <param name="pattern"> 要匹配的正则表达式模式 </param>
        /// <returns> 一个集合，包含有关匹配项的字符串值 </returns>
        public static IEnumerable<string> Matches(this string value, string pattern)
        {
            if (value == null)
            {
                return new string[] { };
            }
            MatchCollection matches = Regex.Matches(value, pattern);
            return from Match match in matches select match.Value;
        }

        /// <summary>
        /// 在指定的输入字符串中匹配第一个数字字符串
        /// </summary>
        public static string MatchFirstNumber(this string value)
        {
            MatchCollection matches = Regex.Matches(value, @"\d+");
            if (matches.Count == 0)
            {
                return string.Empty;
            }
            return matches[0].Value;
        }

        /// <summary>
        /// 在指定字符串中匹配最后一个数字字符串
        /// </summary>
        public static string MatchLastNumber(this string value)
        {
            MatchCollection matches = Regex.Matches(value, @"\d+");
            if (matches.Count == 0)
            {
                return string.Empty;
            }
            return matches[matches.Count - 1].Value;
        }

        /// <summary>
        /// 在指定字符串中匹配所有数字字符串
        /// </summary>
        public static IEnumerable<string> MatchNumbers(this string value)
        {
            return Matches(value, @"\d+");
        }

        /// <summary>
        /// 检测指定字符串中是否包含数字
        /// </summary>
        public static bool IsMatchNumber(this string value)
        {
            return IsMatch(value, @"\d");
        }

        /// <summary>
        /// 检测指定字符串是否全部为数字并且长度等于指定长度
        /// </summary>
        public static bool IsMatchNumber(this string value, int length)
        {
            Regex regex = new Regex(@"^\d{" + length + "}$");
            return regex.IsMatch(value);
        }


        /// <summary>
        /// 用正则表达式截取字符串
        /// </summary>
        public static string Substring2(this string source, string startString, string endString)
        {
            return source.Substring2(startString, endString, false);
        }

        /// <summary>
        /// 用正则表达式截取字符串
        /// </summary>
        public static string Substring2(this string source, string startString, string endString, bool containsEmpty)
        {
            if (source.IsNullOrWhiteSpace())
            {
                return string.Empty;
            }
            string inner = containsEmpty ? "\\s\\S" : "\\S";
            string result = source.Match(string.Format("(?<={0})([{1}]+?)(?={2})", startString, inner, endString));
            return result.IsNullOrWhiteSpace() ? null : result;
        }

        /// <summary>
        /// 是否电子邮件
        /// </summary>
        public static bool IsEmail(this string value)
        {
            const string pattern = @"^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$";
            return value.IsMatch(pattern);
        }

        /// <summary>
        /// 是否是整数
        /// </summary>
        public static bool IsNumeric(this string value)
        {
            const string pattern = @"^\-?[0-9]+$";
            return value.IsMatch(pattern);
        }


        /// <summary>
        /// 是否身份证号，验证如下3种情况：
        /// 1.身份证号码为15位数字；
        /// 2.身份证号码为18位数字；
        /// 3.身份证号码为17位数字+1个字母
        /// </summary>
        public static bool IsIdentityCardId(this string value)
        {
            if (value.Length != 15 && value.Length != 18)
            {
                return false;
            }
            Regex regex;
            string[] array;
            DateTime time;
            if (value.Length == 15)
            {
                regex = new Regex(@"^(\d{6})(\d{2})(\d{2})(\d{2})(\d{3})_");
                if (!regex.Match(value).Success)
                {
                    return false;
                }
                array = regex.Split(value);
                return DateTime.TryParse(string.Format("{0}-{1}-{2}", "19" + array[2], array[3], array[4]), out time);
            }
            regex = new Regex(@"^(\d{6})(\d{4})(\d{2})(\d{2})(\d{3})([0-9Xx])$");
            if (!regex.Match(value).Success)
            {
                return false;
            }
            array = regex.Split(value);
            if (!DateTime.TryParse(string.Format("{0}-{1}-{2}", array[2], array[3], array[4]), out time))
            {
                return false;
            }
            //校验最后一位
            string[] chars = value.ToCharArray().Select(m => m.ToString()).ToArray();
            int[] weights = { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 };
            int sum = 0;
            for (int i = 0; i < 17; i++)
            {
                int num = int.Parse(chars[i]);
                sum = sum + num * weights[i];
            }
            int mod = sum % 11;
            string vCode = "10X98765432";//检验码字符串
            string last = vCode.ToCharArray().ElementAt(mod).ToString();
            return chars.Last().ToUpper() == last;
        }

        /// <summary>
        /// 是否手机号码
        /// </summary>
        /// <param name="value"></param>
        /// <param name="isRestrict">是否按严格格式验证</param>
        public static bool IsMobileNumber(this string value, bool isRestrict = false)
        {
            string pattern = isRestrict ? @"^[1][3-8]\d{9}$" : @"^[1]\d{10}$";
            return value.IsMatch(pattern);
        }

        /// <summary>
        /// 判断是否中文
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool IsChinese(this string value)
        {
            const string pattern = @"[^\x00-\xff]";
            return value.IsMatch(pattern);
        }
        /// <summary>
        /// 判断输入的字符串是否只包含数字和英文字母
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static bool IsNumAndEnCh(this string input)
        {
            if (input.IsNullOrWhiteSpace())
            {
                return false;
            }
            string pattern = @"[\u4E00-\u9FFF]+";
            Regex regex = new Regex(pattern);
            return regex.IsMatch(input);
        }
        #endregion

        /// <summary>
        /// 泛型string值类型转换，高性能
        /// </summary>
        /// <typeparam name="TOut"></typeparam>
        /// <param name="str"></param>
        /// <returns></returns>
        public static TOut ParseTo<TOut>(this string str) where TOut : struct
        {
            try
            {
                return (TOut)Convert.ChangeType(str, typeof(TOut));
            }
            catch
            {
                throw new NotSupportedException(string.Format("The string of \"{0}\" can not be parsed to {1}", str, typeof(TOut)));
            }
        }

        public static sbyte? ToSByte(this string value)
        {
            sbyte value2;
            if (sbyte.TryParse(value, out value2))
            {
                return value2;
            }
            return null;
        }

        public static byte? ToByte(this string value)
        {
            byte value2;
            if (byte.TryParse(value, out value2))
            {
                return value2;
            }
            return null;
        }

        public static ushort? ToUInt16(this string value)
        {
            ushort value2;
            if (ushort.TryParse(value, out value2))
            {
                return value2;
            }
            return null;
        }

        public static short? ToInt16(this string value)
        {
            short value2;
            if (short.TryParse(value, out value2))
            {
                return value2;
            }
            return null;
        }

        public static uint? ToUInt32(this string value)
        {
            uint value2;
            if (uint.TryParse(value, out value2))
            {
                return value2;
            }
            return null;
        }

        public static ulong? ToUInt64(this string value)
        {
            ulong value2;
            if (ulong.TryParse(value, out value2))
            {
                return value2;
            }
            return null;
        }

        public static long? ToInt64(this string value)
        {
            long value2;
            if (long.TryParse(value, out value2))
            {
                return value2;
            }
            return null;
        }

        public static float? ToSingle(this string value)
        {
            float value2;
            if (float.TryParse(value, out value2))
            {
                return value2;
            }
            return null;
        }

        public static double? ToDouble(this string value)
        {
            double value2;
            if (double.TryParse(value, out value2))
            {
                return value2;
            }
            return null;
        }

        public static decimal? ToDecimal(this string value)
        {
            decimal value2;
            if (decimal.TryParse(value, out value2))
            {
                return value2;
            }
            return null;
        }

        /// <summary>
        /// 字符串年月日拼接时分秒返回DateTime
        /// </summary>
        /// <param name="value"></param>
        /// <param name="time"></param>
        /// <returns></returns>
        /// <exception cref="InvalidOperationException"></exception>
        public static DateTime GetCombinedDateTime(this string value,TimeOnly time)
        {
            if (DateOnly.TryParse(value, out DateOnly date))
            {
                return date.ToDateTime(time); // 合并为 DateTime
            }
            //如果转换失败返回默认值
            return default;
        }

        public static decimal ToDecimal(this string value, decimal defaultValue)
        {
            decimal value2;
            if (decimal.TryParse(value, out value2))
            {
                return value2;
            }
            return defaultValue;
        }

        /// <summary>
        /// 布尔型
        /// </summary>
        /// <param name="value"></param>
        /// <param name="res"></param>
        /// <returns></returns>
        public static bool ToBoolean(this string value, bool res = true)
        {
            bool value2;
            if (bool.TryParse(value, out value2))
            {
                return value2;
            }
            return res;
        }

        /// <summary>
        /// 枚举转换
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="str"></param>
        /// <returns></returns>
        public static T? ToEnum<T>(this string str) where T : struct
        {
            T t;
            if (Enum.TryParse(str, true, out t) && Enum.IsDefined(typeof(T), t))
            {
                return t;
            }
            return null;
        }

        /// <summary>
        /// 生成guid
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static Guid? ToGuid(this string str)
        {
            Guid value;
            if (Guid.TryParse(str, out value))
            {
                return value;
            }
            return null;
        }

        /// <summary>
        /// 字符串转时间
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static DateTime? ToDateTime(this string value)
        {
            DateTime value2;
            if (DateTime.TryParse(value, out value2))
            {
                return value2;
            }
            return null;
        }

        /// <summary>
        /// 字符串转int
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static int? ToInt32(this string input)
        {
            if (string.IsNullOrEmpty(input))
            {
                return null;
            }
            int value;
            if (int.TryParse(input, out value))
            {
                return value;
            }
            return null;
        }

        /// <summary>
        /// 字符串转数字(附带默认值)
        /// </summary>
        /// <param name="input">需转换的</param>
        /// <param name="DefaultValue">转换失败默认值</param>
        /// <returns></returns>
        public static int ToInt32(this string input, int DefaultValue)
        {
            if (string.IsNullOrEmpty(input))
            {
                return DefaultValue;
            }
            int value;
            if (int.TryParse(input, out value))
            {
                return value;
            }
            return DefaultValue;
        }


        /// <summary>
        /// 替换空格字符
        /// </summary>
        /// <param name="input"></param>
        /// <param name="replacement">替换为该字符</param>
        /// <returns>替换后的字符串</returns>
        public static string ReplaceWhitespace(this string input, string replacement = "")
        {
            return string.IsNullOrEmpty(input) ? null : Regex.Replace(input, "\\s", replacement, RegexOptions.Compiled);
        }

        /// <summary>
        /// 屏蔽联系方式
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static string ReplaceContactContent(this string str)
        {
            str = Regex.Replace(str, "(tel|t|phone|telephone)+(\\s*)[:]+[\\s0-9-()（）]{7,18}", "******", RegexOptions.IgnoreCase);
            str = Regex.Replace(str, @"(\s*)[0-9-()\s*]{5,18}", "******", RegexOptions.IgnoreCase);
            //str = Regex.Replace(str, ".{10,20}LTD", "******", RegexOptions.IgnoreCase);
            str = Regex.Replace(str, "www\\..+?\\..{2,5}", "******", RegexOptions.IgnoreCase);
            str = Regex.Replace(str, "(skype)+(\\s*)[:][\\s0-9a-z]{3,5}", "******", RegexOptions.IgnoreCase);
            str = Regex.Replace(str, @"\w[-\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\.)+[A-Za-z]{2,14}", "******", RegexOptions.IgnoreCase);
            return str;
        }

        /// <summary>
        /// 随机数
        /// </summary>
        /// <param name="length">长度</param>
        /// <returns></returns>
        public static string RandomNumber(int length = 6)
        {
            char[] constant = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
            StringBuilder newRandom = new StringBuilder(length);
            Random rd = new Random();
            for (int i = 0; i < length; i++)
            {
                newRandom.Append(constant[rd.Next(constant.Length)]);
            }

            return newRandom.ToString();
        }
        /// <summary>
        /// 替换特殊字符为 :-
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static string RepalceSpecialChar(this string value)
        {
            if (value.IsNullOrWhiteSpace())
            {
                return "";
            }
            return Regex.Replace(value, @"[^\w-]+", "-");
        }

        /// <summary>
        /// 获取时间差距描述
        /// </summary>
        /// <param name="timeSpan"></param>
        /// <returns></returns>
        /// <summary>
        /// 获取时间差距描述
        /// </summary>
        /// <param name="timeSpan"></param>
        /// <returns></returns>
        public static string GetTimeSpanStr(this TimeSpan timeSpan)
        {
            var DayStr = "";
            var HoursStr = "";
            var MinutesStr = "";
            if (timeSpan.Days != 0)
            {
                DayStr = $"{timeSpan.Days}天";
            }
            if (timeSpan.Hours != 0)
            {
                HoursStr = $"{timeSpan.Hours}小时";
            }
            if (timeSpan.Minutes != 0)
            {
                MinutesStr = $"{timeSpan.Minutes}分钟";
            }
            if (timeSpan.Days == 0 && timeSpan.Hours == 0 && timeSpan.Minutes == 0)
            {
                MinutesStr = $"{timeSpan.TotalSeconds}秒";
            }
            return DayStr + HoursStr + MinutesStr;
        }

        /// <summary>
        /// 隐藏邮箱前缀
        /// </summary>
        /// <param name="email"></param>
        /// <param name="hideChar"></param>
        /// <returns></returns>
        public static string HideEmailPrefix(this string email, string hideChar = "")
        {
            string result = string.Empty;
            if (!email.IsNullOrWhiteSpace())
            {
                result = email.Contains('@') ? $"{hideChar}{email.Split('@')[1]}" : string.Empty;
            }
            return result;
        }

        /// <summary>
        /// 截取指定长度的字符串
        /// </summary>
        /// <param name="str">要截取的字符串</param>
        /// <param name="len">要截取的长度</param>
        /// <param name="flag">截取后是否加省略号(true加,false不加)</param>
        /// <returns></returns>
        public static string CutString(this string str, int len, bool flag)
        {
            System.Text.ASCIIEncoding ascii = new System.Text.ASCIIEncoding();
            byte[] bts = ascii.GetBytes(str);
            int _len = 0;
            string _outString = "";
            for (int i = 0; i < bts.Length; i++)
            {
                if ((int)bts[i] == 63)//两个字符
                {
                    _len += 2;
                    if (_len > len)//截取的长度若是最后一个占两个字节，则不截取
                    {
                        break;
                    }
                }
                else
                {
                    _len += 1;
                }

                try
                {
                    _outString += str.Substring(i, 1);
                }
                catch
                {
                    break;
                }

                if (_len >= len)
                {
                    break;
                }
            }
            if (str != _outString && flag == true)//判断是否添加省略号
            {
                _outString += "...";
            }
            return _outString;
        }

        /// <summary>
        /// 替换图片链接
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static string ToZhaoChemImg(this string s)
        {
            if (string.IsNullOrEmpty(s))
            {
                s = "";
            }
            s = s.ToLower();
            s = s.Replace("lookchemmall.com", "lookchem.com");
            s = s.Replace("luech.com", "lookchem.com");
            return s;
        }

        /// <summary>
        /// 手机号码(中间替换为*)
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static string MobileToSafe(this string s)
        {
            if (!s.IsMobileNumber())
            {
                return s;
            }
            return s.Substring(0, 3) + "****" + s.Substring(7);
        }
        /// <summary>
        /// 验证字符串是否为有效的 YYYY-MM-DD 格式日期（格式+逻辑双验证）
        /// </summary>
        /// <param name="dateString">日期字符串</param>
        /// <param name="separator">分隔符（默认'-'）</param>
        /// <returns>是否合法日期</returns>
        public static bool IsValidIsoDate(this string dateString, char separator = '-')
        {
            // 空值快速失败
            if (string.IsNullOrWhiteSpace(dateString)) return false;

            // 动态生成正则表达式（根据分隔符）
            var regexPattern = $@"^
            (?<year>\d{{4}})                           # 年
            \{separator}                                # 分隔符
            (?<month>0[1-9]|1[0-2])                     # 月
            \{separator}                                # 分隔符
            (?<day>0[1-9]|[12]\d|3[01])                 # 日
        $";

            var regex = new Regex(regexPattern,
                RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled);

            // 第一步：格式校验
            if (!regex.IsMatch(dateString)) return false;

            // 第二步：逻辑日期验证
            return DateTime.TryParseExact(
                dateString,
                $"yyyy{separator}MM{separator}dd",
                CultureInfo.InvariantCulture,
                DateTimeStyles.None,
                out _
            );
        }

        #region 其他操作

        /// <summary>
        /// 指示指定的字符串是 null 或者 System.String.Empty 字符串
        /// </summary>
        public static bool IsNullOrEmpty(this string value)
        {
            return string.IsNullOrEmpty(value);
        }

        /// <summary>
        /// 指示指定的字符串是 null、空或者仅由空白字符组成。
        /// </summary>
        public static bool IsNullOrWhiteSpace(this string value)
        {
            return string.IsNullOrWhiteSpace(value);
        }
        #endregion

        #region 银行卡替换**

        /// <summary>
        /// 加密银行卡号，格式化为 **** **** **** 1234
        /// </summary>
        /// <param name="bankCardNumber">银行卡号（支持带空格/横线格式）</param>
        /// <returns>脱敏后的安全字符串</returns>
        public static string MaskBankCard(this string bankCardNumber)
        {
            if (string.IsNullOrWhiteSpace(bankCardNumber))
                return "****"; // 空值返回默认值

            // 移除所有非数字字符
            string cleaned = Regex.Replace(bankCardNumber, @"[^\d]", "");

            // 长度校验
            if (cleaned.Length < 4)
                return new string('*', cleaned.Length).PadRight(4, '*'); // 不足4位补全

            // 提取后四位
            string lastFour = cleaned.Substring(cleaned.Length - 4);

            // 通用格式化（不考虑原卡号分段规则）
            return $"**** **** **** {lastFour}";
        }
        #endregion
    }
}
